package cn.tedu.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * 需求: 模拟一个认证场景
 * 1. 假设登录之后的前端在访问业务时,总是会携带一个叫做token的参数
 * http://127.0.0.1:10000/cart/hello?name=王翠花&token=123123123
 * 2. 请创建一个过滤器 验证请求是否携带了这个token,如果携带了是否满足格式要求,都满足 认证通过,过滤器放行,如果认证没通过,过滤器直接反馈响应数据 认证失败.
 * 3. 认证失败逻辑有以下几种情况
 *   1. 没有携带票据
 *   2. 携带了票据,但是没值
 *   3. 携带了票据,有值,但是不满足格式(正则判断是否是10个数字d{10})
 * 代码逻辑:
 * - 使用请求对象 获取token值(获取Authorization头 Cookie: token=123456) 做逻辑判断,判断成功(认证通过,过滤器放行)
 * - 如果判断没通过 ,使用响应对象 输出一个认证失败的信息.
 */
//@Component
public class TokenAuthenticationFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("进入我的认证过滤器");
        //1. 获取token值 ，从查询参数中获取 ；getFirst("token") 是 Spring Framework 中的一个方法，用于从 HTTP 请求参数中获取指定名称的第一个参数值
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        //如果查询参数中没有token，则尝试从header中获取
        if (token == null || token.isEmpty()){
            String authorizationHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
            //验证它是否符合标准的Bearer Token格式（以 "Bearer " 开头）
            if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
                token = authorizationHeader.substring(7); //如果是，则提取Bearer Token的值（跳过前7个字符："Bearer "）
            }
        }
        //如果header中没有token，则尝试从cookie中获取
        if (token == null || token.isEmpty()) {
            String cookieHeader = exchange.getRequest().getHeaders().getFirst("Cookie");
            //cookieHeader.contains("token") 是Java字符串的一个方法调用，用于检查Cookie头字符串中是否包含指定的子字符串
            if (cookieHeader != null && cookieHeader.contains("token")) {
                // 简单解析cookie字符串
                String[] cookies = cookieHeader.split(";");
                for (String cookie : cookies) {
                    if (cookie.startsWith("token=")) {
                        token = cookie.substring(6);//截取token值
                        break;
                    }
                }
            }
        }

        //2.认证逻辑判断
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");

        // 情况1: 没有携带票据
        if (token == null) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            String errorMsg = "{\"code\": 401, \"message\": \"认证失败：未携带token参数\"}";
            DataBuffer buffer = response.bufferFactory().wrap(errorMsg.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer));
        }

        // 情况2: 携带了票据,但是没值
        if (token.isEmpty()) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            String errorMsg = "{\"code\": 401, \"message\": \"认证失败：token值为空\"}";
            DataBuffer buffer = response.bufferFactory().wrap(errorMsg.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer));
        }

        // 情况3: 携带了票据,有值,但是不满足格式要求
        if (!token.matches("\\d{10}")) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            String errorMsg = "{\"code\": 401, \"message\": \"认证失败：token格式不正确，必须为10位数字\"}";
            DataBuffer buffer = response.bufferFactory().wrap(errorMsg.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer));
        }

        // 认证通过，放行请求
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
